Skip to content

[ENG-3999] Support IPv6 filer resolvers in drive mounts - #260

Merged
cploujoux merged 2 commits into
mainfrom
codex/eng-3999-sandbox-ipv6-filer
Aug 18, 2026
Merged

[ENG-3999] Support IPv6 filer resolvers in drive mounts#260
cploujoux merged 2 commits into
mainfrom
codex/eng-3999-sandbox-ipv6-filer

Conversation

@SystemSculpt

@SystemSculpt SystemSculpt commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

  • accept IPv4 and IPv6 nameservers when resolving the Agent Drive filer
  • preserve SeaweedFS's host:http.grpc address format for both address families
  • cover IPv4, production-shaped IPv6, zoned IPv6, malformed fallback, and directive matching

Root cause

Sandbox drive mounts treated the first resolver in /etc/resolv.conf as the filer, but validated it by requiring four dot-separated components. Current Blaxel sandboxes receive IPv6-only resolver addresses (for example 2600:1f14:c75:3900::301), so the parser rejected the valid filer address before blfs could start. The SDK integration failures were downstream symptoms of that sandbox-api defect.

The change uses netip.ParseAddr instead of an IPv4 shape check. It keeps the raw IPv6 literal in SeaweedFS's host:http.grpc representation; SeaweedFS splits on the final colon and brackets the host when it constructs HTTP and gRPC endpoints.

Verification

  • GOTOOLCHAIN=go1.25.0 SHELL=/bin/sh go test -count=1 ./...
  • GOTOOLCHAIN=go1.25.0 go test -race -count=1 ./src/handler/drive
  • GOTOOLCHAIN=go1.25.0 go vet ./...
  • GOTOOLCHAIN=go1.25.0 GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./...

The test matrix proves the former IPv6 input now resolves while IPv4 behavior remains unchanged. Drive listing already splits mount sources at the final colon, so IPv6 mount sources remain compatible there as well.

Scope

This changes only filer-address discovery and formatting for drive mounts. It does not change DNS injection, networking, drive authorization, retries, or error suppression.

Linear: ENG-3999


Note

Merge of main into the IPv6 filer branch, incorporating mount-lock serialization, createMountPoint/chownMountPoint security hardening, workload identity UID/GID defaulting, cross-mount cache coherence flag, and comprehensive tests for all new helpers — alongside the original IPv6 netip.ParseAddr change.

Written by Mendral for commit 1c60e83.


Note

Medium Risk
Touches the drive mount startup path where a bad address breaks all mounts, but the change is narrow and covered by unit tests; IPv4 behavior is preserved.

Overview
Fixes Agent Drive mounts when sandboxes use an IPv6-only first nameserver in /etc/resolv.conf. Filer discovery no longer requires a four-part dotted IPv4 shape; it uses netip.ParseAddr on the first valid nameserver line (including zoned IPv6) and skips malformed entries.

blfs -filer= is built via formatFilerServerAddress, which keeps SeaweedFS’s host:http.grpc form (ports 49200.49201) with unbracketed IPv6 literals, matching how SeaweedFS splits on the final colon.

Logic is split into testable parseFilerAddress; new tests cover IPv4/IPv6, zones, malformed fallback, and address formatting.

Reviewed by Cursor Bugbot for commit 1c60e83. Bugbot is set up for automated code reviews on this repo. Configure here.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🧪 Testing Guide

What this PR addresses

Sandbox drive mounts failed on IPv6-only environments because the filer address parser in getFilerAddress() only accepted IPv4 nameservers (validated by checking for four dot-separated components). Production Blaxel sandboxes now receive IPv6 resolver addresses (e.g. 2600:1f14:c75:3900::301), causing blfs mount to never start.

This PR:

  • Replaces the IPv4-only validation with net/netip.ParseAddr, accepting both IPv4 and IPv6.
  • Extracts parseFilerAddress and formatFilerServerAddress as testable helpers.
  • Preserves SeaweedFS's host:http.grpc address format for both address families.

Steps to reproduce the original issue

  1. Deploy a sandbox in an environment where /etc/resolv.conf contains only an IPv6 nameserver (e.g. nameserver 2600:1f14:c75:3900::301).
  2. Attempt to mount an Agent Drive (trigger MountDrive).
  3. Before this PR: The mount fails with "no valid nameserver found in /etc/resolv.conf" because the IPv6 address doesn't have four dot-separated parts.

What to verify (expected behavior)

  1. Unit tests pass:

    cd sandbox-api && go test ./src/handler/drive/ -run "TestParseFilerAddress|TestFormatFilerServerAddress" -v

    All 6 TestParseFilerAddress cases (IPv4, IPv6, zoned IPv6, malformed fallback, directive mismatch, missing nameserver) and both TestFormatFilerServerAddress cases (IPv4 and IPv6) should pass.

  2. IPv4 environments still work: In a sandbox with an IPv4 resolver, drive mounts continue to work as before (no regression).

  3. IPv6 environments now work: In a sandbox with an IPv6-only resolver, the filer address is correctly parsed and the -filer flag is formatted as <ipv6>:49200.49201 — SeaweedFS handles bracket-wrapping internally.

  4. Format correctness: Verify that formatFilerServerAddress produces:

    • 172.16.1.126:49200.49201 for IPv4
    • 2600:1f14:c75:3900::301:49200.49201 for IPv6 (unbracketed, per SeaweedFS's parser convention)
  5. No other /etc/resolv.conf directives are misinterpreted: Lines like nameserver-proxy or options should be skipped (covered by tests).

Note

Posted by PR Testing Guide · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

✅ Linked to Linear issue ENG-3999 — status already In Progress.

Note

Posted by Linear Issue Enforcer · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Interaction Flow

sequenceDiagram
    participant MD as MountDrive()
    participant GFA as getFilerAddress()
    participant PFA as parseFilerAddress()
    participant RC as /etc/resolv.conf
    participant FMT as formatFilerServerAddress()
    participant SW as SeaweedFS (blfs)

    MD->>GFA: resolve filer address
    GFA->>PFA: parse resolv.conf content
    PFA->>RC: read nameserver entries
    RC-->>PFA: nameserver lines (IPv4 or IPv6)
    PFA->>PFA: netip.ParseAddr() validates address
    PFA-->>GFA: valid IP string (v4 or v6)
    GFA-->>MD: filer address
    MD->>FMT: format server address
    FMT->>FMT: append ":httpPort.grpcPort"
    FMT-->>MD: "host:49200.49201"
    MD->>SW: mount with filer server address
Loading

Summary

The PR refactors the drive mount filer resolution to support IPv6 nameservers in /etc/resolv.conf. The key flow is:

  1. MountDrive calls getFilerAddress() which now delegates to the new parseFilerAddress() for parsing resolv.conf entries.
  2. parseFilerAddress() uses netip.ParseAddr() (replacing manual dot-split validation) to accept both IPv4 and IPv6 addresses, including zone identifiers.
  3. The validated address is passed to the new formatFilerServerAddress() which appends SeaweedFS's host:http.grpc port format.
  4. The formatted address is used to mount the drive via SeaweedFS (blfs).

This ensures sandboxes with IPv6-only resolvers can successfully mount Agent Drives.

Note

Posted by PR Sequence Diagram · Tag @mendral-app with feedback.

mendral-app[bot]

This comment was marked as outdated.

@SystemSculpt
SystemSculpt marked this pull request as draft July 27, 2026 20:08
@SystemSculpt

Copy link
Copy Markdown
Member Author

Parking this pending owner review — @cploujoux, this is in your lane (ENG-3999), so handing it over rather than merging it myself.

What is verified:

  • merges cleanly with current main; go build ./..., go vet ./... and go test ./src/handler/drive all pass
  • the full sandbox-api suite shows only mkdir /var/log/sandbox-api: permission denied failures in src/handler/process, and those reproduce identically on origin/main (local non-root environment, unrelated to this change)
  • the IPv4 path is unchanged: the format string is still %s:49200.49201 and netip.ParseAddr(...).String() returns the same literal for IPv4. The old four-dot check also accepted invalid input such as 1.2.3.junk, so this is strictly tighter
  • no overlap with cploujoux/sandbox-api-ipv6-autoforward, which adds src/lib/networking/dualstack.go on the listener side and does not touch getFilerAddress

What is NOT verified, and why I stopped:

  • that blfs/SeaweedFS actually mounts with an unbracketed IPv6 filer. The split-on-final-colon reasoning is sound but it is an argument, not an execution — and that is the entire point of the change
  • I tried to verify on dev first and could not: three sandboxes in a dev workspace (blaxel/base-image:latest x2, blaxel/node:latest x1, eu-dub-1) all reached FAILED. Cause is scheduling, not this PR:
reason: FailedScheduling
0/9 nodes are available: 2 node(s) didn't match Pod's node affinity/selector,
7 node(s) had untolerated taint(s). no new claims to deallocate,
preemption: 0/9 nodes are available: 9 Preemption is not helpful for scheduling.

Pod sbx-eng3999-probe3-gokind, namespace dev-onboarder, cluster executionplanev3-dev-eu-west-1, label blaxel-node-type-override: kraft. All probe sandboxes were deleted.

One thing to weigh before merging: a push to main sets BL_ENV=prod in build.yaml, builds mk3 images into the prod image bucket, and runs publish-sandbox.sh with TAG: latest, which controlplane picks up automatically. Worst case if the IPv6 assumption is wrong is that IPv6 mounts keep failing; IPv4 behaviour is provably unchanged.

mount_test.go conflicted add/add: both sides created the file with different
tests. Resolved as a union — main's mount point, path and lock tests alongside
this branch's IPv6 filer tests. mount.go merged cleanly, keeping netip-based
parsing next to main's cross-mount cache and mount-lock changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cploujoux
cploujoux marked this pull request as ready for review August 18, 2026 17:33

@mendral-app mendral-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

My previous LGTM had no comments to address. The IPv6 parsing logic is unchanged. The merged-in code from main (mount locking, symlink-safe chown, idempotent mount detection) is well-structured with correct lock discipline — unmountDriveLocked is only called while already holding the per-path lock. Test coverage for the new helpers is thorough.

Tag @mendral-app with feedback or questions. View session

@cploujoux
cploujoux merged commit da3b3b6 into main Aug 18, 2026
8 of 10 checks passed
@cploujoux
cploujoux deleted the codex/eng-3999-sandbox-ipv6-filer branch August 18, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants